
//Copyright 1997-2009 Syrinx Development, Inc.
//This file is part of the Syrinx Web Application Framework (SWAF).
// == BEGIN LICENSE ==
//
// Licensed under the terms of any of the following licenses at your
// choice:
//
//  - GNU General Public License Version 3 or later (the "GPL")
//    http://www.gnu.org/licenses/gpl.html
//
//  - GNU Lesser General Public License Version 3 or later (the "LGPL")
//    http://www.gnu.org/licenses/lgpl.html
//
//  - Mozilla Public License Version 1.1 or later (the "MPL")
//    http://www.mozilla.org/MPL/MPL-1.1.html
//
// == END LICENSE ==
using System;
using System.Collections;
using System.Threading;

using Swaf;
using Swaf.BizRule;
using Swaf.Call;
using Swaf.Container;

namespace Swaf.BizRule.ThreadedQueue
{
	/// <summary>
	/// Summary description for BasicRuleGroup.
	/// </summary>
	public class BasicRuleGroup : BasicExecutor, IRuleGroup, IRuleGroupControl
	{
		protected IRuleGroupControl m_parentGroup;
		protected LinkedList m_childRuleGroups;
		protected int m_totalRules = 0;
		protected int m_rulesCompleted = 0;

		//This class attempts to lazy create these semaphore objects, and as a result they may never actually
		//get created.  They are only ever attempted to be created when a call to waitForCompletion occurs and their
		//are outstanding rules running at the depth requested in the wait.  If that does occur and their needs to
		//be a wait the semaphore is created and the wait sits on it.  If a call to waitForCompletion occurs 
		//after all the previously executed rules have completed, these semaphores never get created or used.
		protected Semaphore m_groupWaitSem;
		protected Semaphore m_deepGroupWaitSem;
		//protected object m_ruleCompleteLock = new object();

		public BasicRuleGroup(IRuleGroupControl parentGroup)
		{
			m_parentGroup = parentGroup;
		}

		protected override IApplication App
		{
			get
			{
				return MainRequest.App;
			}
		}

		protected override IRuleGroup ParentRuleGroup
		{
			get
			{
				return this;
			}
		}

		protected internal override SingleCallAndResult getRuleInfo(string ruleName)
		{
			SingleCallAndResult rc = base.getRuleInfo (ruleName);
			return rc;
		}



		protected Semaphore getWaitSem(bool deepWait)
		{
			if(deepWait)
			{
				if(m_deepGroupWaitSem == null)
					m_deepGroupWaitSem = new Semaphore(0);
				return m_deepGroupWaitSem;
			}
			else
			{
				if(m_groupWaitSem == null)
					m_groupWaitSem = new Semaphore(0);
				return m_groupWaitSem;
			}
		}

		internal SingleCallAndResult ownerWantsRuleInfo(string ruleName, object callParam)
		{
			SingleCallAndResult rc = base.getRuleInfo(ruleName);
			rc.setResults(callParam, null);
			return rc;
		}
		#region IRuleGroup Members

		/// <summary>
		/// This method can wait.
		/// </summary>
		/// <remarks>This method is designed to have many threads call into it concurrently.  If rules in the 
		/// depth specified as still running, this method will block all callers to this method.  As soon the 
		/// more rules are executing again, the semaphore is locked and calls block again.
		/// <p>This method attempts to avoid using the semaphores for blocking unless absolutely necessary.
		/// If there are no rules running, it wont try to block on it.  If there are rules, it will attempt
		/// to block on the given semaphore type and then</p></remarks>
		/// <param name="deepWait"></param>
		public virtual void waitForCompletion(bool deepWait)
		{
			int x = getNotDoneRuleCount(deepWait);
			if(x != 0)
			{
				Worker w = (Worker)Thread.GetData(((RuleQueue)RuleQueue).WorkerSlot);
				if(w != null)
				{

					x = getNotDoneRuleCount(deepWait);
					while(x > 0)
					{
						x = getNotDoneRuleCount(deepWait);
						w.processSingleElement(false);
					}
					return;

				}

				try
				{
					/*
					Semaphore s = getWaitSem(deepWait);
					x = getNotDoneRuleCount(deepWait);
					if(x != 0)
					{
						s.acquire();
						s.release();
					}
					*/

					
					int loop = 15;
					do
					{
						try
						{
							Semaphore s = getWaitSem(deepWait);
							x = getNotDoneRuleCount(deepWait);
							if(x != 0)
							{
								if(s.acquire(1000))
								{
									s.release();
									break;
								}
								//else
								//	--loop;
							}
							else
								break;
						}
						catch(Exception e)
						{
							//TODO: Log
						}
					}while(loop >= 0);
					
					
				}
				catch (Exception e)
				{
					//Cmn.writeLog(e.ToString());
				}

				//getWaitSem(deepWait).acquire();
			}
		}

		public IInitialRequest MainRequest
		{
			get
			{
				if(m_parentGroup is IInitialRequest)
					return (IInitialRequest)m_parentGroup;
				else
					return m_parentGroup.Group.MainRequest;
			}
		}

		public void continueExecution()
		{
			// TODO:  Add BasicRuleGroup.continueExecution implementation
		}

		public void pauseExecution()
		{
			// TODO:  Add BasicRuleGroup.pauseExecution implementation
		}

		public void cancelExecution()
		{

		}

		public int TotalRules
		{
			get
			{
				return m_totalRules;
			}
		}

		public int RulesCompletedCount
		{
			get
			{
				return m_rulesCompleted;
			}
		}

		public virtual IRuleGroup createChildGroup(bool maintainResults)
		{
			IRuleGroup childGroup;
			if(maintainResults)
				childGroup = new ResultsRuleGroup(this);
			else
				childGroup = new BasicRuleGroup(this);
			addChildRuleGroup(childGroup);
			return childGroup;
		}

		#endregion

		public void addChildRuleGroup(IRuleGroup child)
		{
			lock(this)
			{
				ChildRuleGroups.addTail(child);
			}
		}

		#region IResults Members
		public int numberOfResults {get{return 0;}}
		public object CallResults {get{return null;}}
		public object getCallResults(int index) {return null;}
		public Exception ExceptionInfo {get{return null;}}
		public string getXml(){return "";}
		public Exception getExceptionInfo(int index) {return null;}
		#endregion

		#region IRuleGroupControl Members

		public void ruleCompleted()
		{
			Interlocked.Increment(ref m_rulesCompleted);
			calcWaitGroup(null);
		}

		public IRuleGroup Group 
		{
			get
			{
				return this;
			}
		}

		public IRuleGroup AnonymousGroup()
		{
			return this;
		}

		public void calcWaitGroup(IRuleGroup child)
		{
			//lock(m_ruleCompleteLock)
			//lock(this)
			//{
			int deepNum = getNotDoneRuleCount(true);
			if(deepNum == 0 && m_parentGroup != null)
				m_parentGroup.calcWaitGroup(this);

			if(m_deepGroupWaitSem != null)
				if(deepNum == 0)
				{
					getWaitSem(true).releaseAll();
				}
				else
					getWaitSem(true).acquireAll(0);

			if(m_groupWaitSem != null)
				if(getNotDoneRuleCount(false) == 0)
					getWaitSem(false).releaseAll();
				else
					getWaitSem(false).acquireAll(0);
			//}
		}

		public int getNotDoneRuleCount(bool deep)
		{
			if(!deep)
				return m_totalRules - m_rulesCompleted;
			else
			{
				int num = m_totalRules - m_rulesCompleted;
				if(m_childRuleGroups != null)
				{
					IRuleGroupControl child = null;
					LinkedList.NodeRef head = ChildRuleGroups.Head;
					while (head != null)
					{
						child = (IRuleGroupControl) head.Element;
						num+= child.getNotDoneRuleCount(deep);
						head = head.Next;
					}
					//					foreach(IRuleGroupControl child in ChildRuleGroups)
					//						num += child.getNotDoneRuleCount(deep);
				}
				return num;
			}
		}

		protected LinkedList ChildRuleGroups
		{
			get
			{
				lock(this)
				{
					if(m_childRuleGroups == null)
						m_childRuleGroups = new LinkedList();
					return m_childRuleGroups;
				}
			}
		}

		public void ruleAdded()
		{
			Interlocked.Increment(ref m_totalRules);
		}

		#endregion
	}
}
